Text Scanning
Many XML elements can have the optional attribute scan . This defines a region of the letter from which to scan the text. The region is defined as X,Y,W,H where X,Y are the coordinates of the Top-Left corner of the region, measured in mm from the Top-Left corner of the page, and W,H are the width and height of the region in mm.
Schema
<nodeName [scan="X,Y,W,H"]
[simple="true|false"]
[page="number"]
[rightOf="{ref}"]
[leftOf="{ref}"]
[extract|extractLast="{ref}" [includeRef="true|false"]]
[numericOnly="true|false"]
[requiredChars="length"]
[requiredLastChars="length"]
[requiredMidChars="start,length"]
[date|dateTime="true|false|future" [dateFormat="format-string"] [mustHave="true|false"]]
[joinLines="true|false"]
[splitLines="split-text"]
[replace="find-text~replace-text"]
[matching="regex"]>regular-expression</nodeName>
Scan modifier attributes
Any XML element supporting the scan attribute implicitly also supports the optional simple and page attributes, as well as all the optional post-processing attributes, as well as default literal text. N.B. If the literal text is enclosed in curly braces, then it specifies the name of an identifier. In this case the value of that identifier will be used as the default.
All attributes are optional
Both the scan attribute and the element literal text can be present together. If so, the literal text is used if the scan region is empty.
<nodeName>literaltext</nodeName> <!-- use the literal text -->
<nodeName>{ident-name}</nodeName> <!-- use the value of the identifier named 'ident-name' -->
<nodeName scan="20,89,70,8"/> <!-- scan text from letter -->
<nodeName scan="20,89,70,8">literaltext</nodeName> <!-- scan text from letter, but use literal text if scan region is empty -->
<nodeName scan="20,89,70,8">{my-ident}</nodeName> <!-- scan text from letter, but use the value of the identifier named 'my-ident' if scan region is empty -->
<nodeName scan="X,Y,W,H" simple="true">
If true, the scanned text will be examines using a 'simple' instead of 'location' strategy. 'simple' retrieves the text in the order that it was added to the document; 'location' retrieves it in the order visible to the reader. The default is 'Location'
<nodeName scan="X,Y,W,H" page="number">
If present, the text will be scanned from the specified page. Use positive numbers to indicate the ‘nth’ page from the start of the letter and negative numbers for the ‘nth’ page from the end of the letter E.g. page="-2" means scan from the second-last page. N.B. The page must exist! The default is page 1.
Scan post-processing attributes
These optional attributes are applied after the text has been scanned. They also apply to the literal text if the scan region was empty.
All attributes are optional
Very Important!!
Regardless of the order in which they are written in the XML file, the attributes are processed in the order shown in the schema; and their effect is cummulative.
<nodeName rightOf="ref"/> and <nodeName leftOf="ref" />
If present, the scanned text will be examines for 'ref' and only the text to the right (or left) of the first occurrence of 'ref' will be returned. If 'ref' does not appear in the scanned text, an empty string is returned.
| Account: 12345WM00 | rightOf="123" | 45WM00 |
| Account: 12345WM00 | leftOf="45" | Account: 123 |
<nodeName extract="ref" /> and <nodeName extractLast="ref" />
If present, the scanned text is examined for 'ref' and only the text following 'ref' up to the end of the line will be returned. If 'ref' does not appear in the scanned text, an empty string is returned. extractLast is the same except that it searches for the last instance of 'ref'. If the includeRef="true" attribute is also present, then the 'ref' is also returned.
Name: Fred Account: 12345WM00 Name: George Account: 12346WM01 | extract="Account:" | 12345WM00 |
Name: Fred Account: 12345WM00 Name: George Account: 12346WM01 | extractLast="Account:" | 12346WM01 |
Name: Fred Account: 12345WM00 Name: George Account: 12346WM01 | extract="Account:" includeRef="true" | Account: 12345WM00 |
<nodeName numericOnly="true|false" />
If present, the scanned text, stripped of all non-numeric characters, is returned.
| Account: 12345WM00 | numericOnly="true" | 1234500 |
<nodeName requiredChars="length" and requiredLastChars="length" />
If present, only the first (or last) "length" characters of the scanned text is returned. If "
length" is larger than the length of the scanned text, then the entire scanned text is returned. If "length" is negative, then everything except the first or last "length" characters of the scanned text is returned.
| 12345WM00 | requiredChars="3" | 123 |
| 12345WM00 | requiredChars="-4" | 5WM00 |
| 12345WM00 | requiredLastChars="3" | M00 |
| 12345WM00 | requiredLastChars="-6" | 123 |
<nodeName requiredMidChars="start,length" />
If present, only "length" characters starting from "start" of the scanned text are returned. If "start" is larger than the length of the scanned text, then an empty string is returned. The returned string is never longer than the scanned text even if start + length exceeds the scanned text length.
| 12345WM00 | requiredMidChars="3,4" | 45WM |
<nodeName date="false|true|future" /> and <nodeName dateTime="false|true|future" />
If present, the scanned text is examined for a string that represents a valid date. If such a string is found, it is returned. If no valid date is found, then the entire scanned text is returned, unless the mustHave=true attribute is present, in which case an empty string is returned. If the attribute value is "true" then the first instance of a valid date is returned. If the attribute value is "future" then the first instance of a valid date in the future is returned.
dateTime is similar to date except that it searches for and returns the time as well as the date. If a valid date is found, but no valid time, then the date string is returned with the time component set to 00:00.
If the scanned text represents a valid date (and time), the additional attribute dateFormat="format-string" can be added to format the returned text according to the format-string. The format string can contain new line character "\n" if desired.
Following your last appointment on 14 Aug 2021, your next appointment is on the 21st September 2022 at 14:00 | date="true" | 14 August 2021 |
Following your last appointment on 14 Aug 2021, your next appointment is on the 21st September 2022 at 14:00 | date="future" | 21 September 2022 |
Following your last appointment on 14 Aug 2021, your next appointment is on the 21st September 2022 at 14:00 | dateTime="true" | 14 August 2021 00:00 |
Following your last appointment on 14 Aug 2021, your next appointment is on the 21st September 2022 at 14:00 | dateTime="future" | 21 September 2022 14:00 |
Your next appointment is on the 21st September 2022 at 14:00 | dateTime="future" dateFormat="dddd dd MMMM yyyy at hh:mm" | Monday 21 September 2022 at 14:00 |
Your next appointment is on the 21st September 2022 at 14:00 | dateTime="future" dateFormat="dd/MM/yy hh:mm" | 21/09/22 14:00 |
N.B Searching for dates and times is a slower operation than normal text post-processing so, if used, it is best to combine this attribute with others so as to restrict the size of the text before examining it for a valid date.
<nodeName joinLines="true|false" />
If present, all lines in the scanned text are joined into one line, and then all multiple-whitespace is reduced to single spaces.
Name: Fred Name: George Name: Harry | joinLines="true" | Name: Fred Name: George Name: Harry |
<nodeName splitLines="split-text" />
If present, a new line character "\n" is inserted before every instance of split-text.
| Name: Fred Name: George Name: Harry | splitLines="Name" | Name: Fred Name: George Name: Harry |
<nodeName replace="find-text~replace-text" />
If present, every instance of "find-text" is replaced by "replace-text". N.B. the tilde character ~ is mandatory and is used as a separator for the "find-text" and "replace-text". Use "\n" to represent a new line character in either the "find-text" or "replace-text" if desired.
Name: Fred Name: George Name: Harry | replace="Name~Firstname" | Firstname: Fred Firstname: George Firstname: Harry |
<nodeName matching="regex" />
If present, the first instance of a substring in the scanned text that matches the regular expression is extracted.
| The postcode is TA1 1JJ and the number is 7 | matching="[A-Z][A-Z][1-9] [1-9][A-Z][A-Z]" | TA1 1JJ |